﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows;

namespace Scheduler.Presentation_Layer
{
    /**Class: EmployeePreferences
     * @author Sara Tuohy
     * @version 1.0
     * Course : ITEC 4860 Spring 2012
     * Written: March, 2012
     * This Form accesses employee preferences
     * Purpose: to allow employees to set or change preferences
     */
    public partial class EmployeePreferences : Form
    {
        /**
         * Convert day to mnemonic                                          */
        private string[] daysOfWeek = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" };
        /**
         * Character limit for comments box.  Changed to 1000 due to 
         * field length and Employee table limits                           */
        private const int CHARACTER_LIMIT = 1000;
        /**
         * Constant for loop control                                        */
        private const int NUM_WEEK_DAYS = 7;
        /**
         * ID of the person logged in                                       */
        private string currentUser;
        /**
         * Adapter for DB access                                            */
        private Data_Access_Layer.EmpDayInfo employeeDay;
        /**
         * Adapter for DB access                                            */
        private Data_Access_Layer.Employee employeeData;
        /**
         * Storing controls in arrays makes them easy to access             */
        private CheckBox[] preferredDays;
        private Button[] setPrefsButtons;
        /**
         * OperationWeek holds the company hours of business                */
        private OperationDay[] operationWeek;
        /**
         * Global access of hours of operation means it's only coded once                    */
        private bool officeHoursSet = Scheduler.Presentation_Layer.GlobalVariables.GlobalHoursOfOperationSet;
        /**
         * An array used to convert between hours of operation and 48 all day time slots             */
        string[] allTimeSlots;

        /**Method: EmployeePreferences
         * Constructor
         */
        public EmployeePreferences()
        {
            InitializeComponent();
            currentUser = Scheduler.Presentation_Layer.GlobalVariables.GlobalLoggedInUser;
            allTimeSlots = GlobalVariables.GlobalAllTimeSlots;
            setUpArrays();
            if (!GlobalVariables.GlobalHoursOfOperationSet)
            {
                Business_Logic_Layer.StaffingRequirements staffingData = new Business_Logic_Layer.StaffingRequirements();
                staffingData.loadDBData();
            }

            operationWeek = Scheduler.Presentation_Layer.GlobalVariables.GlobalOperationWeek;
            setComboBoxRanges();
            setMaxMinHours();
            checkDataCurrency();
        }

        /**Method: setUpArrays()
         * Put start time, end time combo boxes and 
         * perferred days check boxes into arrays.
         * This allows easy access as we loop through days.
         */
        private void setUpArrays()
        {
            preferredDays = new CheckBox[NUM_WEEK_DAYS];
            setPrefsButtons = new Button[NUM_WEEK_DAYS]; ;

            preferredDays[0] = epSunPreferredChkBox;
            preferredDays[1] = epMonPreferredChkBox;
            preferredDays[2] = epTuePreferredChkBox;
            preferredDays[3] = epWedPreferredChkBox;
            preferredDays[4] = epThuPreferredChkBox;
            preferredDays[5] = epFriPreferredChkBox;
            preferredDays[6] = epSatPreferredChkBox;

            setPrefsButtons[0] = setSunAvlButton;
            setPrefsButtons[1] = setMonAvlButton;
            setPrefsButtons[2] = setTueAvlButton;
            setPrefsButtons[3] = setWedAvlButton;
            setPrefsButtons[4] = setThuAvlButton;
            setPrefsButtons[5] = setFriAvlButton;
            setPrefsButtons[6] = setSatAvlButton;

        }

        /**Method: empPrefHomeButton
         * Button to close panel and return to caller
         */
        private void empPrefHomeButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        /**Method: setComboBoxRanges
         * Sets the ranges for the combo boxes
         * Purpose: set up start and end time combo boxes, and enable the 
         * appropriate preferred days check boxes.
         */
        private void setComboBoxRanges()
        {
            if (Scheduler.Presentation_Layer.GlobalVariables.GlobalHoursOfOperationSet)
            {
                int strIdx, numSlots;
                string[] openHours;
                operationWeek = Scheduler.Presentation_Layer.GlobalVariables.GlobalOperationWeek;
                for (int i = 0; i < NUM_WEEK_DAYS; i++)
                {
                    if (operationWeek[i].isOpen())
                    {
                        strIdx = operationWeek[i].getStartIndex();
                        numSlots = (operationWeek[i].getEndIndex() - strIdx);
                        //add 1 to make room for the time it closes
                        openHours = new string[numSlots + 1];
                        Array.Copy(allTimeSlots, strIdx, openHours, 0, numSlots);
                        openHours[numSlots] = "";
                        openHours[numSlots] = operationWeek[i].getEnd();
                        preferredDays[i].Enabled = true;
                        setPrefsButtons[i].Enabled = true;
                    }
                }
            }
        }

        /**Method: getStoredData
         * Read DB for employee preferences and update the GUI
         */
        private void getStoredData()
        {
            setMaxMinHours();
            for (int i = 0; i < NUM_WEEK_DAYS; i++)
            {
                employeeDay = new Data_Access_Layer.EmpDayInfo(currentUser, GlobalVariables.GlobalWeekDays[i]);
                object[] dayData;
                if (employeeDay.locateEmpDay())
                {
                    int strIdx, numSlots;
                    int startCBIdx, endCBIdx;
                    dayData = (object[])employeeDay.getAllAvailHr();
                    int[] startEndPtrs = checkAvlTimes(dayData);
                    if (startEndPtrs[0] >= 0)
                    {
                        startCBIdx = startEndPtrs[0];
                        endCBIdx = startEndPtrs[1];
                        startCBIdx -= 2;       //The DB array starts at [2]; correct that offset
                        endCBIdx -= 2;         //The DB array starts at [2]; correct that offset
                        strIdx = startCBIdx - operationWeek[i].getStartIndex();
                        numSlots = endCBIdx - startCBIdx;
                    }               //end if start pointer >= 0
                }                   //end if locateEmpDay
            }                       //end for i=0 to i<7
        }                           //end getStoredData()

        /**Method: setMaxMinHours
         * Reads the DB and sets the values in the text boxes
         */
        private void setMaxMinHours()
        {
            employeeData = new Data_Access_Layer.Employee(currentUser);
            if (employeeData.locateEmployee())
            {
                int maxHours = employeeData.getEmpMaxWeeklyHrs();
                epMaxHrsComboBox.Text = maxHours.ToString();
                int minHours = employeeData.getEmpMinWeeklyHrs();
                epMinHrsComboBox.Text = minHours.ToString();
            }
        }

        /**Method: checkAvlTimes
         * Returns the positions in the DB array of the start and end times indicated by employee
         * @param object[] dbArray - array from the data base "getAllAvailHr()" call
         * @return int[] int[0] = start time position; int[1] = end time position.  If negative, does not
         * want to work that day.
         */
        private int[] checkAvlTimes(object[] dayInfo)
        {
            int[] startEndPoints = new int[2];
            int startPtr = -1, endPtr = -1;
            startPtr = getPointer(dayInfo, 2, true);
            if (startPtr >= 0)                                          //employee has indicated he wants to work, so
            {
                endPtr = getPointer(dayInfo, startPtr, false);          //get the end time
                if (endPtr < 0)
                {
                    endPtr = dayInfo.Length;                            //if not found, works till midnight
                }
            }
            startEndPoints[0] = startPtr;
            startEndPoints[1] = endPtr;
            return startEndPoints;
        }

        /**Method: getPointer
         * Identifies the start and end times (start is the first bool=true; end is first bool=false)
         * Accepts the array from the data base (position 0 and 1 are day and employee ID, then 48
         * boolean entries indicating available or not).  When looking for start, "true" is passed as the
         * bool lookFor; if found, index is returned; if not found, -1 is returned.  When looking for end, 
         * "false" is passed as lookFor, and idx = start.  If a subsequent false is found, the position is
         * returned, otherwise it is assumed he wants to work to end of the day (midnight).
         * @param object[] dbArray - array from the data base "getAllAvailHr()" call
         * @param int idx - either 2 (to find start) or start (to find end)
         * @param bool lookFor - either true (to find start) or false (to find end)
         * @return int ptr value for the start or end pointer or -1 if not found
         */
        private int getPointer(object[] dbArray, int idx, bool lookFor)
        {
            bool endNotReached = (idx < dbArray.Length), goodToGo, tempBool;
            string boolString;
            int ptr = -1;
            while ((ptr < 0) && (endNotReached))
            {
                goodToGo = false;
                tempBool = false;

                //Made a small change here Sally, Still need to set some to "P" for preference
                boolString = dbArray[idx].ToString().Trim();
                if (boolString.Equals("N"))
                {
                    tempBool = false;
                    goodToGo = true;
                }
                if (boolString.Equals("A"))
                {
                    tempBool = true;
                    goodToGo = true;
                }

                if (goodToGo)
                {
                    if (tempBool.Equals(lookFor))
                    {
                        ptr = idx;
                    }
                }
                idx++;
                endNotReached = (idx < dbArray.Length);
            }
            return ptr;
        }

        /**Method: checkDataCurrency
         * Compares the "last saved" values (from the DB) of the hours of Operation and
         * the Employee Preferences; if the hours were updated since the last time the 
         * Employee updated his preferences, he is advised to review his choices.
         */
        private void checkDataCurrency()
        {
            string Co_ID = GlobalVariables.GlobalCompanyID;
            string day = "Sunday";
            Data_Access_Layer.StaffingRequirements staffReq = new Data_Access_Layer.StaffingRequirements(day, Co_ID);
            Data_Access_Layer.Employee employeeData = new Data_Access_Layer.Employee(currentUser);
            if (staffReq.locateOperatingDay() && employeeData.locateEmployee())
            {
                string hoursLastChanged = staffReq.getTimeChanged();
                string preferencesLastChanged = employeeData.getPreferencesLastUpdated();
                if (hoursChangedSinceLastPreferenceUpdate(hoursLastChanged, preferencesLastChanged))
                {
                    string info = "ALERT! Hours Of Operation have been changed since the last time" + "\n";
                    info += "your preferences were saved.  Any new open hours will have a " + "\n";
                    info += "default value of 'Available'.  Please review your preferences " + "\n";
                    info += "to ensure your wishes are correctly indicated. " + "\n" + "\n";
                    info += "This alert will repeat until your preferences are saved again.";
                    MessageBox.Show(info, "Hours Changed Alert", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
                else
                {
                    //preserved diagnostic message ...
                    //MessageBox.Show("HrsChg, PrfChg = " + hoursLastChanged + ", " + preferencesLastChanged, "Info",
                    //    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }
            else
            {
                string msg = "Unable to verify when hours of operation were last updated; " +
                    "\n" + "your preferences may be out of date.";
                MessageBox.Show(msg,"Currency Check",MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        /**Method: hoursChangedSinceLastPreferenceUpdate
         * Compares hours saved / preferences saved timestamps and returns true if the 
         * hours of operation update was the more recent of the two
         * If the conversion to int fails the employee is advised to verify his settings
         * @param DateTime hoursChanged - date/time the hours of operation were saved
         * @param DateTime prefChanged - date/time of the last preferences saved operation.
         * @return bool true if the hours update was the more recent 
         */
        private bool hoursChangedSinceLastPreferenceUpdate(string hoursChanged, string prefsChanged)
        {
            long hrsChg = 0, prfChg = 0;
            try
            {
                hrsChg = Convert.ToInt64(hoursChanged);
                prfChg = Convert.ToInt64(prefsChanged);
            }
            catch
            {
                string errorMsg = "Error verifying when Hours of Operation were last updated. " + "\n"
                    + "Your preferences may be out of date.";
                MessageBox.Show(errorMsg, "Notify Support if Error Continues", MessageBoxButtons.OK, MessageBoxIcon.Error);
                //hrsChg = 1; //will return "true" so user only receives one notification
            }
            return (hrsChg > prfChg);
        }

        /**Method: ePrefSaveButton_Click
         * Validate Input
         * Purpose: validate input before saving to DB
         * Note: DB save not yet implemented
         */
        private void ePrefSaveButton_Click(object sender, EventArgs e)
        {
            storeMaxMin();
        }                   //end ePrefSaveButton_Click

        /**Method: storeMaxMin
         * Store the indicated Max/Min Weekly hours in the db
         * These values come from a combo box with previously
         * validated values, so we skip the try/catch here
         */
        private void storeMaxMin()
        {
            if (epMaxHrsComboBox.SelectedIndex == -1 ||
                epMinHrsComboBox.SelectedIndex == -1)
            {
                MessageBox.Show("Cannot save until Minimum and Maximum Hours have been set.",
                    "Save Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                employeeData = new Data_Access_Layer.Employee(currentUser);
                if (employeeData.locateEmployee())
                {
                    string maxHoursStr = epMaxHrsComboBox.Text;
                    string minHoursStr = epMinHrsComboBox.Text;
                    int maxHours = Convert.ToInt16(maxHoursStr);
                    int minHours = Convert.ToInt16(minHoursStr);
                    employeeData.setEmpMaxWeeklyHrs(maxHours);
                    employeeData.setEmpMinWeeklyHrs(minHours);
                    string currentDateTime = GlobalVariables.GlobalCurrentDateTime;
                    //MessageBox.Show(currentDateTime);  //save diagnostic
                    employeeData.setPreferencesLastUpdated(currentDateTime);
                    if ((maxHours == employeeData.getEmpMaxWeeklyHrs()) &&
                        (minHours == employeeData.getEmpMinWeeklyHrs()))
                    {
                        string msg = "Desired Minimum and Maximum Hours saved successfully.";
                        MessageBox.Show(msg, "Save Results",
                            MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                    else
                    {
                        MessageBox.Show("Error: Save Unsuccessful", "Save Results",
                            MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        /**Method: atLeastOneDayPreferred
         * Verify at least one day is indicated "preferred"
         * Purpose: satisfy requirement
         * @return bool true if box is checked, else return false
         */
        private bool atLeastOneDayPreferred()
        {
            for (int i = 0; i < NUM_WEEK_DAYS; i++)
            {
                if (preferredDays[i].Checked)
                {
                    return true;
                }
            }
            return false;
        }


        /**Method: errorNotification
         * Alerts user to error
         * Purpose: reusable code to notify user of problem and set
         * sentinel to false, to terminate the verification loop 
         * pending error correction.
         * @param string errorMsg - message to display
         */
        private void errorNotification(string errorMsg)
        {
            MessageBox.Show(errorMsg, "Save Error!",
                  MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            //notCancelled = false;
        }

        /**Method: setAvlButton_Click
         * Called by all the set preferences buttons, calls a panel to let the employee specify his preferences per time slot
         * (he can indicate he is "Available", "Not Available" or "Preferred")
         */
        private void setAvlButton_Click(object sender, EventArgs e)
        {
            //we use sender to identify the button that called this function, then equate that
            //with a day.  We use that to get the index to the operationWeek, and pull the start/end
            //time information for that day.  
            Button dayButton = (Button)sender;
            string day = dayButton.Name.Substring(3, 3).ToUpper();
            int dayNum = Array.IndexOf(daysOfWeek, day);
            OperationDay thisOperatingDay = operationWeek[dayNum];
            int startIndex = thisOperatingDay.getStartIndex();
            int arrayLength = (thisOperatingDay.getEndIndex() - thisOperatingDay.getStartIndex());
            string[] openHours = new string[arrayLength];
            Array.Copy(allTimeSlots, startIndex, openHours, 0, arrayLength);
            EmployeePrefPerTimeSlot empAvlSlotPanel =
                new EmployeePrefPerTimeSlot(openHours, thisOperatingDay);
            empAvlSlotPanel.ShowDialog();

        }       //End of setAvlButton_Click

    }                   //end of class
}
